//
// Created by alexa on 5/13/2024.
//

#include "CrewsService.h"
#include <unordered_map>

#include "../../exceptions/EntityNotFound.h"

MaintenanceCrew *CrewsService::getById(string id) {
    MaintenanceCrew *crew = repository.findById(id);
    if (!crew) {
        throw EntityNotFound("Maintenance crew with this id was not found.");
    }
    return crew;
}

vector<MaintenanceCrew *> CrewsService::getAll() {
    return repository.findAll();
}

MaintenanceCrew *CrewsService::createOne() {
    MaintenanceCrew *crew = new MaintenanceCrew();
    saveCrew(crew);
    return crew;
}

void CrewsService::deleteOne(string id) {
    MaintenanceCrew *crew = getById(id);
    CombatUnit* unit = unitsService->findByCrewId(id);
    if (unit) {
        unit->setCrewId("");
        unitsService->saveUnit(unit);
    }
    repository.deleteOne(crew);
}

CrewsService::CrewsService(SoldiersService *soldiersService) : soldiersService(soldiersService) {}

void CrewsService::addSoldier(string crewId, string soldierId) {
    MaintenanceCrew *crew = getById(crewId);
    MaintenanceCrew *prevCrew = findBySoldierId(soldierId);
    Soldier *soldier = soldiersService->getById(soldierId);
    if (prevCrew) {
        prevCrew->removeSoldierId(soldierId);
        saveCrew(crew);
    }
    crew->addSoldierId(soldier->getId());
    saveCrew(crew);
}

void CrewsService::removeSoldier(string crewId, string soldierId) {
    MaintenanceCrew *crew = getById(crewId);
    Soldier *soldier = soldiersService->getById(soldierId);
    crew->removeSoldierId(soldier->getId());
    saveCrew(crew);
}

vector<Soldier *> CrewsService::getSoldiersInCrew(string crewId) {
    MaintenanceCrew *crew = getById(crewId);
    vector<Soldier *> members = soldiersService->getManyByIds(crew->getSoldiersIds());
    return members;
}

MaintenanceCrew *CrewsService::findBySoldierId(string soldierId) {
    vector<MaintenanceCrew *> allCrews = getAll();
    for (const auto &crew: allCrews) {
        vector<string> ids = crew->getSoldiersIds();
        vector<string>::iterator id = find(ids.begin(), ids.end(), soldierId);
        if (id != ids.cend()) return crew;
    }
    return nullptr;
}

vector<Specialization>
CrewsService::getMissingSpecializationsInCrew(string crewId, vector<Specialization> requiredSpecializations) {
    MaintenanceCrew *crew = getById(crewId);

    vector<Specialization> currentSpecializations;
    vector<Soldier *> members = soldiersService->getManyByIds(crew->getSoldiersIds());

    for (const auto &soldier: members) {
        currentSpecializations.push_back(soldier->getSpecialization());
    }

    unordered_map<Specialization, int> curSpecsCount;
    unordered_map<Specialization, int> requiredSpecsCount;

    for (auto const spec: requiredSpecializations) {
        if (!requiredSpecsCount.contains(spec)) requiredSpecsCount[spec] = 0;
        requiredSpecsCount[spec]++;
    }

    for (auto const spec: currentSpecializations) {
        if (!curSpecsCount.contains(spec)) curSpecsCount[spec] = 0;
        curSpecsCount[spec]++;
    }

    unordered_map<Specialization, int> missingSpecsCount;

    for (auto const entry: requiredSpecsCount) {
        if (!curSpecsCount.contains(entry.first)) {
            missingSpecsCount[entry.first] = entry.second;
            continue;
        }

        missingSpecsCount[entry.first] = entry.second - curSpecsCount[entry.first];
    }

    vector<Specialization> missingSpecializations;

    for (const auto entry: missingSpecsCount) {
        if (entry.second <= 0) continue;
        for (int i = 0; i < entry.second; i++) {
            missingSpecializations.push_back(entry.first);
        }
    }

    return missingSpecializations;
}

void CrewsService::saveCrew(MaintenanceCrew *crew) {
    repository.save(crew);
}

void CrewsService::setUnitsService(UnitsService *unitsService) {
    this->unitsService = unitsService;
}


